package me.flyray.bsin.gateway.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.alipay.sofa.rpc.common.json.JSON;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.flyray.bsin.cache.BsinCacheProvider;
import me.flyray.bsin.constants.ResponseCode;
import me.flyray.bsin.context.LoginInfoContextHelper;
import me.flyray.bsin.exception.BusinessException;
import me.flyray.bsin.gateway.utils.HttpHelper;
import me.flyray.bsin.utils.BsinServiceInvokeUtil;

/**
 * @author ：bolei
 * @date ：Created in 2021/12/17 13:02
 * @description：
 * @modified By：
 */

@Slf4j
public class SignValidationFilter implements Filter {

    public BsinServiceInvokeUtil bsinServiceInvokeUtil;

    private BsinCacheProvider bsinCacheProvider;

    public SignValidationFilter(BsinServiceInvokeUtil bsinServiceInvokeUtil) {
        this.bsinServiceInvokeUtil = bsinServiceInvokeUtil;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        ServletContext servletContext = filterConfig.getServletContext();
        WebApplicationContext cxt = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        if (bsinCacheProvider == null) {
            bsinCacheProvider = (BsinCacheProvider) cxt.getBean("bsinCacheProvider");
        }
    }

    @SneakyThrows
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException, BusinessException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        // 防止流读取一次后就没有了, 所以需要将流继续写出去
        ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
        String ip = getIpAddress(httpServletRequest);
        log.info("请求IP-------------------{}", ip);
        // 判断一个ip如果在一分钟内调用20次就
        Long beginTime = System.currentTimeMillis();
        if (StringUtils.isNotBlank(bsinCacheProvider.get("blacklist:" + ip))) {
            // 将异常分发到/error/exthrow控制器
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
            request.getRequestDispatcher("/limitingError?code=" + ResponseCode.API_LIMITING.getCode() + "&message=" + ResponseCode.API_LIMITING.getMessage()).forward(request, httpServletResponse);
            return;
        }
        if (StringUtils.isNotBlank(bsinCacheProvider.get(ip))) {
            long oldTime = Long.valueOf(bsinCacheProvider.get(ip));
            long dValue = beginTime - oldTime;
            // 如果ip在黑名单里面则禁止调用
//            if (dValue < 50) {
//                bsinCacheProvider.setEx("blacklist:" + ip, String.valueOf(true), Long.valueOf(60));
//                // 将异常分发到/error/exthrow控制器
//                HttpServletResponse httpServletResponse = (HttpServletResponse) response;
//                httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
//                request.getRequestDispatcher("/limitingError?code=" + ResponseCode.FAIL.getCode() + "&message=" + ResponseCode.FAIL.getMessage()).forward(request, httpServletResponse);
//                return;
//            }
        }
        bsinCacheProvider.set(ip, String.valueOf(beginTime));

        try {
            //获取用户凭证
            //=================获取json格式的签名字段=========================
            String body = HttpHelper.getBodyString(requestWrapper);
            // 此处是一个大坑，浏览器发起的一个请求上来，会进去两次，第一次body数据为空，只需要放过，postman等工具发起的只会进入一次
            if (StringUtils.isNotBlank(body)) {
                JSONObject bodyData = JSONObject.parseObject(body);
                //如果是app获取版本信息跳过签名
                String serviceMethod = String.valueOf(bodyData.get("methodName"));
                Map<String, String> bizParams = (Map<String, String>) bodyData.get("bizParams");
                if (serviceMethod.equals("getMinAppInfo") || "test".equals(bizParams.get("chainEnv"))) {
                    chain.doFilter(requestWrapper, response);
                    return;
                }
                // 根据appId获取appSecret验证签名

                String sign = (String) bodyData.get("sign");
                String timestamp = (String) bodyData.get("timestamp");
                bizParams.put("sign", sign);
                bizParams.put("timestamp", timestamp);

                if (StringUtils.isBlank(sign)) {
                    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
                    httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
                    request.getRequestDispatcher("/sign?code=" + ResponseCode.SIGN_NOT_EMPTY.getCode() + "&message=" + ResponseCode.SIGN_NOT_EMPTY.getMessage()).forward(request, httpServletResponse);
                    return;
                }
                //TODO 根据appId调用RPC服务获取appSecret验证签名,后期优化从缓存获取
                Map requestMap = new HashMap();
                String tenantId = "";
                String merchantNo = "";
                String tokenTenantId = LoginInfoContextHelper.getTenantId();
                String tokenMerchantNo = LoginInfoContextHelper.getMerchantNo();
                log.info("tokenTenantId-----: {}", tokenTenantId);
                log.info("merchantNo-----: {}", tokenMerchantNo);
                // TODO 此处存在线程变量错乱的情况
                if (StringUtils.isBlank(tokenMerchantNo)) {
                    tenantId = bizParams.get("tenantId");
                    merchantNo = bizParams.get("merchantNo");
                } else {
                    log.info("tokenTenantId 不为空-----: {}", tokenTenantId);
                    log.info("appId-----: {}", tokenMerchantNo);
                    tenantId = tokenTenantId;
                    merchantNo = tokenMerchantNo;
                }
                log.info("tenantId-----: {}", tenantId);
                log.info("merchantNo-----: {}", merchantNo);

                // 根据appId获取请求应用信息
                requestMap.put("merchantNo", merchantNo);
                requestMap.put("tenantId", tenantId);
                Map resultMap = getBiganMerchantApp(requestMap,request,response);

                Map dataMap = (Map) resultMap.get("data");
                String signSecret = (String) dataMap.get("signSecret");

            /*if(!SignUtls.verify(bizParams,signSecret)){
                throw new BusinessException(ResponseCode.SIGN_INVALIT);
            }*/
            }

        } catch (Exception e) {
            System.out.println(e);
            // 将异常分发到/error/exthrow控制器
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
            request.getRequestDispatcher("/sign?code=" + ResponseCode.SIGN_INVALIT.getCode() + "&message=" + ResponseCode.SIGN_INVALIT.getMessage()).forward(request, httpServletResponse);
            return;
        }
        chain.doFilter(requestWrapper, response);
    }

    @Override
    public void destroy() {

    }

    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值，第一个ip才是真实ip
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 获取开放平台商户下的信息
     * 商户下的应用
     * @param req
     * @return
     */
    public Map getBiganMerchantApp(Map<String, Object> req, ServletRequest request, ServletResponse response) throws ServletException, IOException {
        Map result = new HashMap();
        try {
            result = bsinServiceInvokeUtil.genericInvoke("MerchantAppService", "getDetail","1.0", req);
            log.info("获取开放平台租户的应用信息:{}", JSON.toJSONString(result));
        } catch (Exception e) {
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
            request.getRequestDispatcher("/sign?code=" + ResponseCode.APP_INFO_ERROR.getCode() + "&message=" + ResponseCode.APP_INFO_ERROR.getMessage()).forward(request, httpServletResponse);
        }
        return result;
    }

}
